﻿using SharpDX;
using System;
using System.IO;

namespace Engine.DynamicObjectManager
{
    /// <summary>
    /// the Object is for storing all object-properties. All Values which could use in
    /// different games should assign to this BaseNode. So they could easy saved by the game specific
    /// classes.
    /// </summary>
    public class BaseObject
    {
        #region Private Fields

        /// <summary>
        /// angle of the object along the Y-axis in radiants.
        /// </summary>
        private float angleY;

        /// <summary>
        /// Object Name
        /// </summary>
        private string name;

        /// <summary>
        /// Position X on terrain.
        /// </summary>
        private float positionX;

        /// <summary>
        /// Position Y on terrain.
        /// </summary>
        private float positionY;

        /// <summary>
        /// Position Z on terrain.
        /// </summary>
        private float positionZ;

        /// <summary>
        /// Scale how the model should be rendered.
        /// </summary>
        private float size;

        #endregion Private Fields

        #region Public Constructors

        public BaseObject(string name, float positionX, float positionY, float positionZ, float angle, float scale)
        {
            this.name = name;
            this.positionX = positionX;
            this.positionY = positionY;
            this.positionZ = positionZ;
            this.angleY = angle;
            this.size = scale;
        }

        public BaseObject()
        {
            this.name = "NoneName";
        }

        #endregion Public Constructors

        #region Public Properties

        /// <summary>
        /// the angle along the Y-axis.
        /// </summary>
        public float AngleY
        {
            get { return this.angleY; }
            set { this.angleY = value; }
        }

        /// <summary>
        /// The name of the object.
        /// </summary>
        public string Name
        {
            get { return this.name; }
            set { this.name = value; }
        }

        /// <summary>
        /// Position X on Terrain.
        /// </summary>
        public float PositionX
        {
            get { return this.positionX; }
            set { this.positionX = value; }
        }

        /// <summary>
        /// Position Y on terrain.
        /// </summary>
        public float PositionY
        {
            get { return this.positionY; }
            set { this.positionY = value; }
        }

        /// <summary>
        /// Position Z on terrain.
        /// </summary>
        public float PositionZ
        {
            get { return this.positionZ; }
            set { this.positionZ = value; }
        }

        /// <summary>
        /// Set the size how big the object should be rendered in game.
        /// </summary>
        public float Size
        {
            get { return this.size; }
            set { this.size = value; }
        }

        #endregion Public Properties

        #region Public Methods

        public static BaseObject FromStream(BinaryReader br)
        {
            switch (br.ReadInt16())
            {
                case 1:
                    return LoadV1(br);
            }
            throw new Exception("Unknown FileFormat!");
        }

        public void ScaleX(float Scale)
        {
            this.positionX *= Scale;
        }

        public void ToStream(BinaryWriter bw)
        {
            bw.Write((short)1); // Version
            bw.Write(this.name);
            bw.Write(this.positionX);
            bw.Write(this.positionY);
            bw.Write(this.positionZ);
            bw.Write(this.angleY);
            bw.Write(this.size);
        }

        /// <summary>
        /// Creates a new BaseObjects with all given transformations.
        /// </summary>
        /// <param name="newPosition"></param>
        /// <param name="angle">angle in degrees.</param>
        /// <param name="scale"></param>
        /// <returns></returns>
        public BaseObject Transform(Vector3 newPosition, float angle, float scale)
        {
            float angleRad = ((float)angle * (float)(Math.PI / 180)) + this.angleY;
            Matrix matrix = Matrix.RotationY((float)angle * (float)(Math.PI / 180));

            Vector3 Position = Vector3.TransformCoordinate(new Vector3(this.PositionX, this.PositionY, this.PositionZ), matrix);

            Position = Vector3.Multiply(Position, scale) + newPosition;

            return new BaseObject(this.name, Position.X, Position.Y, Position.Z, angleRad, 1.0f);
        }

        #endregion Public Methods

        #region Private Methods

        private static BaseObject LoadV1(BinaryReader br)
        {
            BaseObject baseObject = new BaseObject()
            {
                name = br.ReadString(),
                positionX = br.ReadSingle(),
                positionY = br.ReadSingle(),
                positionZ = br.ReadSingle(),
                angleY = br.ReadSingle(),
                size = br.ReadSingle()
            };
            return baseObject;
        }

        #endregion Private Methods
    }
}